/**
	Pulsar engine. Additional include file.
	Additional include file. Logging

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace pl
{

class StreamWrite;
class LogProcessor;
class LogProcessorImpl;

/**
	Standard log levels
*/
enum LogLevel
{
	/// Critical and fatal messages
	LOG_LEVEL_FATAL = 0,
	/// Errors
	LOG_LEVEL_ERROR = 1,
	/// Warnings
	LOG_LEVEL_WARN = 2,
	/// Information messages
	LOG_LEVEL_INFO = 3,
	/// All debugging operations
	LOG_LEVEL_DEBUG = 4,
	_LOG_LEVEL_MAX
};

/**
	Typical data for messages
*/
struct LogData
{
	pl_int32 number;              /// order number
	const pl_char* msg;           /// Text message
	LogLevel level;               /// Message level
	const pl_char* lib;           /// Library identifier (may be NULL for external messages)
	pl_uint32 time;               /// Time in ms
	const pl_char* fileName;      /// Filename where occured (may be NULL)
	pl_int32 lineNmb;             /// Line number where occured (may be NULL)
	const pl_char* function;      /// Function where occured (may be NULL)
	pl_int32 threadId;            /// Thread id
	pl_int32 processId;           /// Process id
};

/*
	Internal macro. Use plLog***() instead
*/
#define _PL_LOG_HELPER(lib, fmt, msg, level, file, line, func, showassert)             \
		do {                                                                           \
			pl::LogProcessor lp(lib, level);                                           \
			if (lp.isPassed()) {                                                       \
				if ((pl_char*)(fmt) == NULL) lp.get() << msg;                          \
				else { lp.get() << fmt; pl::format(lp.get()) << msg; }                 \
				lp.dispatch(file, line, func);                                         \
				if (showassert) PL_ASSERTW(false, lp.get().get());                     \
			}                                                                          \
		} while (false)

/// Debug log from internal library
#define plLogDebug(lib, msg)    _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_DEBUG,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Information (report) log from internal library
#define plLogInfo(lib, msg)     _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_INFO,   PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Warning log from internal library
#define plLogWarn(lib, msg)  _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_WARN,   PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Error log from internal library
#define plLogError(lib, msg)    _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_ERROR,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Fatal/critical log from internal library
#define plLogFatal(lib, msg)    _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_FATAL,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)

/// Debug log from external file
#define plLogExtDebug(file, line, msg)    _PL_LOG_HELPER(NULL, NULL, msg, pl::LOG_LEVEL_DEBUG,  file, line, NULL, false)
/// Information (report) log from external file
#define plLogExtInfo(file, line, msg)     _PL_LOG_HELPER(NULL, NULL, msg, pl::LOG_LEVEL_INFO,   file, line, NULL, false)
/// Warning log from external file
#define plLogExtWarn(file, line, msg)  _PL_LOG_HELPER(NULL, NULL, msg, pl::LOG_LEVEL_WARN,   file, line, NULL, false)
/// Error log from external file
#define plLogExtError(file, line, msg)    _PL_LOG_HELPER(NULL, NULL, msg, pl::LOG_LEVEL_ERROR,  file, line, NULL, false)
/// Fatal/critical log from external file
#define plLogExtFatal(file, line, msg)    _PL_LOG_HELPER(NULL, NULL, msg, pl::LOG_LEVEL_FATAL,  file, line, NULL, false)

/// Debug log from internal library
#define plLogDebugA(lib, msg)    _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_DEBUG,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, true)
/// Information (report) log from internal library
#define plLogInfoA(lib, msg)     _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_INFO,   PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, true)
/// Warning log from internal library
#define plLogWarnA(lib, msg)  _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_WARN,   PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, true)
/// Error log from internal library
#define plLogErrorA(lib, msg)    _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_ERROR,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, true)
/// Fatal/critical log from internal library
#define plLogFatalA(lib, msg)    _PL_LOG_HELPER(lib, NULL, msg, pl::LOG_LEVEL_FATAL,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, true)

/// Debug log from internal library (with formatting)
#define plLogDebugF(lib, fmt, msg)    _PL_LOG_HELPER(lib, fmt, msg, pl::LOG_LEVEL_DEBUG,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Information (report) log from internal library (with formatting)
#define plLogInfoF(lib, fmt, msg)     _PL_LOG_HELPER(lib, fmt, msg, pl::LOG_LEVEL_INFO,   PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Warning log from internal library (with formatting)
#define plLogWarnF(lib, fmt, msg)     _PL_LOG_HELPER(lib, fmt, msg, pl::LOG_LEVEL_WARN,   PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Error log from internal library (with formatting)
#define plLogErrorF(lib, fmt, msg)    _PL_LOG_HELPER(lib, fmt, msg, pl::LOG_LEVEL_ERROR,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)
/// Fatal/critical log from internal library (with formatting)
#define plLogFatalF(lib, fmt, msg)    _PL_LOG_HELPER(lib, fmt, msg, pl::LOG_LEVEL_FATAL,  PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME, false)

/// Debug log from external file (with formatting)
#define plLogExtDebugF(file, line, fmt, msg)    _PL_LOG_HELPER(NULL, fmt, msg, pl::LOG_LEVEL_DEBUG,  file, line, NULL, false)
/// Information (report) log from external file (with formatting)
#define plLogExtInfoF(file, line, fmt, msg)     _PL_LOG_HELPER(NULL, fmt, msg, pl::LOG_LEVEL_INFO,   file, line, NULL, false)
/// Warning log from external file (with formatting)
#define plLogExtWarnF(file, line, fmt, msg)     _PL_LOG_HELPER(NULL, fmt, msg, pl::LOG_LEVEL_WARN,   file, line, NULL, false)
/// Error log from external file (with formatting)
#define plLogExtErrorF(file, line, fmt, msg)    _PL_LOG_HELPER(NULL, fmt, msg, pl::LOG_LEVEL_ERROR,  file, line, NULL, false)
/// Fatal/critical log from external file (with formatting)
#define plLogExtFatalF(file, line, fmt, msg)    _PL_LOG_HELPER(NULL, fmt, msg, pl::LOG_LEVEL_FATAL,  file, line, NULL, false)

#if defined(PL_LOG_INFO) || defined(PL_LOG_WARN) || defined(PL_LOG_ERROR) || defined(PL_LOG_FATAL)

	#undef plLogDebug
	#undef plLogDebugA
	#undef plLogExtDebug
	#undef plLogDebugF
	#undef plLogExtDebugF

	#define plLogDebug(lib, msg)     ((void)0)
	#define plLogDebugA(lib, msg)    ((void)0)
	#define plLogExtDebug(file, line, msg)  ((void)0)
	#define plLogDebugF(lib, fmt, msg)     ((void)0)
	#define plLogExtDebugF(file, line, fmt, msg)  ((void)0)

#endif

#if defined(PL_LOG_WARN) || defined(PL_LOG_ERROR) || defined(PL_LOG_FATAL)

	#undef plLogInfo
	#undef plLogInfoA
	#undef plLogExtInfo
	#undef plLogInfoF
	#undef plLogExtInfoF

	#define plLogInfo(lib, msg)      ((void)0)
	#define plLogInfoA(lib, msg)     ((void)0)
	#define plLogExtInfo(file, line, msg)   ((void)0)
	#define plLogInfoF(lib, fmt, msg)      ((void)0)
	#define plLogExtInfoF(file, line, fmt, msg)   ((void)0)

#endif

#if defined(PL_LOG_ERROR) || defined(PL_LOG_FATAL)

	#undef plLogWarn
	#undef plLogWarnA
	#undef plLogExtWarn
	#undef plLogWarnF
	#undef plLogExtWarnF

	#define plLogWarn(lib, msg)      ((void)0)
	#define plLogWarnA(lib, msg)     ((void)0)
	#define plLogExtWarn(file, line, msg)   ((void)0)
	#define plLogWarnF(lib, fmt, msg)      ((void)0)
	#define plLogExtWarnF(file, line, fmt, msg)   ((void)0)

#endif

#if defined(PL_LOG_ERROR) || defined(PL_LOG_FATAL)

	#undef plLogError
	#undef plLogErrorA
	#undef plLogExtError
	#undef plLogErrorF
	#undef plLogExtErrorF

	#define plLogError(lib, msg)      ((void)0)
	#define plLogErrorA(lib, msg)     ((void)0)
	#define plLogExtError(file, line, msg)   ((void)0)
	#define plLogErrorF(lib, fmt, msg)      ((void)0)
	#define plLogExtErrorF(file, line, fmt, msg)   ((void)0)

#endif


#define DECLARE_ON_LOG_MESSAGE() virtual void onLogMessage(const pl::LogData &params)
#define DECLARE_ON_LOG_MESSAGE2() virtual void onLogMessage2(const pl::LogData &params)
#define DECLARE_ON_LOG_FLUSH() virtual void onLogFlush()

/*
	:pg events subscriber
*/
class ILogTracer : public pl::ListNode<ILogTracer>
{
public:
	static void _dispatch(const pl::LogData &params);
	static void _dispatch2(const pl::LogData &params);

protected:
	inline DECLARE_ON_LOG_MESSAGE()
	{
	}

	inline DECLARE_ON_LOG_MESSAGE2()
	{
	}

	inline DECLARE_ON_LOG_FLUSH()
	{
	}

	/// Ctor
	ILogTracer() :
	pl::ListNode<ILogTracer>(_handlerList)
	{
	}

	static List<ILogTracer> _handlerList;
};

class LogProcessor
{
public:
	explicit LogProcessor(const pl_char* lib, pl::LogLevel level);
	~LogProcessor();

	/// Get a reference to allocated string
	inline pl::String<1024>& get()
	{
		return *_storage;
	}

	/// Check is log message passed
	bool isPassed() const;
	/// Dispatch log message to all active subscribers
	void dispatch(const pl_char* file, pl_int32 line, const pl_char* func);

	/// Setup new log level
	static void setLogLevel(LogLevel level, const pl_char* libName = NULL);

	/// Get text for error code
	static const pl_char* errorLevelNames[_LOG_LEVEL_MAX];

	/// for internal use only: log system initialization
	static void _init();
	/// for internal use only: log system shutdown
	static void _term();
private:
	/// Log processor implementation
	static LogProcessorImpl* _pImpl;
	/// Total message counter
	static int _msgCount;

	/// true if the message must be passed to subscribers
	bool _isPassed;
	/// library identifier
	const pl_char* _lib;
	/// log level
	pl::LogLevel _level;
	/// Internal string
	pl::String<1024>* _storage;
	/// Thread nested counter
	int _threadNestedIndex;
};

} // namespace pl

//
// End of file 'pulsar_log.h'
//
